package com.coolw.thread.demo;

import org.junit.Test;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * CompletableFuture
 * <pre>
 * 1.实现了Future接口，Future接口有的功能，CompletableFuture也有
 * 2.实现了CompletionStage接口，定义了任务编排的方法，执行到某一阶段，可以向下执行后续阶段
 * 3.CompletableFuture相比于Future接口，最大的改进是提供了回调监听功能；也就是当上一阶段任务执行结束后，
 * 可以回调你指定的下一阶段任务，而不需要阻塞获取结果之后再来处理
 * </pre>
 *
 * <p>常用方法
 * <pre>
 * #1.实例化CompletableFuture
 * 1.1 构造方法：new CompletableFuture<>(),需要调用complete方法来表示执行完成，才能获取到结果
 * 1.2 静态方法
 * supplyAsync：有返回值，创建后会立即开启异步任务执行
 * runAsync：无返回值，创建后会立即开启异步任务执行
 * </pre>
 *
 * <p>#2.获取任务结果
 * <pre>
 * 2.1 get()：获取结果，会一直阻塞，直到获取到结果
 * 2.2 get(long timeout, TimeUnit unit)：获取结果，指定阻塞时间。如果超过了指定的时间，则会抛TimeoutException异常
 * 2.3 getNow(T valueIfAbsent)：获取结果，不会阻塞。当任务执行完成则返回任务执行结果，反之返回传入的valueIfAbsent参数值
 * 2.4 join()：跟get()方法基本类似，get()会抛出检查时异常，join()不会
 * </pre>
 *
 * <p>#3.主动触发任务完成
 * <pre>
 * 3.1 complete：主动触发当前异步任务完成，并且返回的任务结果为complete方法的参数值;如果任务未完成，返回true，任务已完成则返回false
 * 3.2 completeExceptionally：跟complete作用差不多，complete是正常结束任务，返回结果；completeExceptionally是任务执行的异常
 * </pre>
 *
 * <p>#4.对任务执行结果进行下一步处理
 * <pre>
 * 4.1 只能接收任务执行正常的回调
 * > thenApply：可以拿到上一步执行的结果，并且返回处理的结果；当thenApply方法执行异常时，是不会回调的
 * > thenRun：拿不到上一步执行的结果，但会执行Runnable接口的实现
 * > thenAccept：可以拿到上一步执行的结果，但不能返回处理的结果
 * 4.2 只能接收任务执行异常的回调
 * > exceptionally：当任务执行异常后，会回调执行exceptionally方法，有返回值；如果没有出现异常，则不会回调
 * 4.3 能同时接收任务执行正常或异常的回调
 * > handle：任务执行正常或异常，handle方法都会执行，有返回值；当任务执行发生异常时，handle能吞了异常
 * > whenComplete：有返回值，主线程能够获取到上个阶段的返回值；whenComplete不能吞了异常，当主线程在获取执行异常的任务结果时，会抛出异常
 * </pre>
 *
 * <p>#5.任务合并
 * <pre>
 * > thenCombine：将当前任务和其他任务都执行结束后，拿到这两个任务的执行结果合并处理，回调BiFunction，然后返回新的结果
 * </pre>
 *
 * <p>#6.以Async结尾的方法
 * <pre>
 * 6.1 一般会有两个Async方法,区别为一个是使用默认的线程池来执行任务（ForkJoinPool）；另一个是方法参数传入的线程池来执行任务
 * 6.2 例如thenApply跟thenApplyAsync方法，主要区别是执行任务是否开启异步线程来执行；thenApplyAsync会重新开启一个线程来执行下一个任务
 * 而thenApply还是用上一阶段任务执行的线程来执行
 * </pre>
 *
 * @author coolw
 * @date 2023/4/6 14:34
 */
public class Demo20CompletableFuture {

    private final ExecutorService executorService = Executors.newFixedThreadPool(5);

    /**
     * 实例化CompletableFuture
     * <ul>
     *      <li>1.构造方法创建。需要调用complete方法来表示任务完成，才能获取到结果</li>
     *      <li>2.静态方法创建。会立即开启异步任务执行</li>
     * </ul>
     */
    @Test
    public void testInstance() throws Exception {
        // 1.构造方法创建
        CompletableFuture<String> completableFuture1 = new CompletableFuture<>();
        // 表示任务执行完成，返回false
        boolean a = completableFuture1.complete("a");
        // 上面已经执行了complete方法，此时任务已经完成，返回true
        boolean b = completableFuture1.complete("b");
        System.out.println("a:" + a + ",b:" + b + ",result:" + completableFuture1.get());

        // 2.静态方法创建
        // supplyAsync：有返回值，会立即开启异步任务执行
        CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> "b");
        System.out.println(completableFuture2.get());
        // runAsync：无返回值，会立即开启异步任务执行
        CompletableFuture<Void> voidCompletableFuture3 = CompletableFuture.runAsync(() -> System.out.println("c"));
        System.out.println(voidCompletableFuture3.get());
    }

    /**
     * thenApply：可以拿到上一步执行的结果，并且返回处理的结果；当thenApply方法执行异常时，是不会回调的
     */
    @Test
    public void testThenApply() throws Exception {
        // 执行正常，则回调
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> 1)
                .thenApply(r -> ("上一步返回的结果:" + r));
        System.out.println(completableFuture.get());

        // 执行异常，不回调
        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            int i = 1 / 0;
            return 2;
        }).thenApply(r -> ("上一步返回的结果:" + r));
        System.out.println(completableFuture1.get());
    }

    /**
     * thenRun：拿不到上一步执行的结果，但会执行Runnable接口的实现
     */
    @Test
    public void testThenRun() throws Exception {
        CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> 2)
                .thenRun(() -> System.out.println("上一步执行完成"));
    }

    /**
     * thenAccept：可以拿到上一步执行的结果，但不能返回处理的结果
     */
    @Test
    public void testThenAccept() {
        CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> 1)
                .thenAccept(r -> System.out.println("上一步执行完成" + r));
    }

    /**
     * exceptionally
     * <ul>
     *      <li>1.当任务执行异常后，会回调执行exceptionally方法，有返回值；如果没有出现异常，则不会回调</li>
     *      <li>2.当任务执行异常时，exceptionally能吞了异常</li>
     * </ul>
     */
    @Test
    public void testExceptionally() throws Exception {
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            // 模拟报错
            return 1 / 0;
        }).exceptionally(e -> {
            System.out.println("任务执行异常，返回默认值0");
            return 0;
        });
        System.out.println(completableFuture.get());
    }

    /**
     * handle
     * <ul>
     *      <li>1.任务执行正常或异常，handle方法都会执行，有返回值</li>
     *      <li>2.当任务执行发生异常时，handle能吞了异常</li>
     * </ul>
     */
    @Test
    public void testHandle() throws Exception {
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            // 模拟报错
            int i = 1 / 0;
            return 1;
        }).handle((r, e) -> {
            System.out.println("handle被调用了;r:" + r + ", e:" + e);
            return r;
        });
        System.out.println(completableFuture.get());
    }

    /**
     * whenComplete
     * <ul>
     *      <li>1.任务执行正常或异常，都会回调执行whenComplete()方法</li>
     *      <li>2.有返回值，主线程能够获取到上个阶段的返回值</li>
     *      <li>3.whenComplete不能吞了异常，当主线程在获取执行异常的任务结果时，会抛出异常</li>
     * </ul>
     */
    @Test
    public void testWhenComplete() throws Exception {
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            // 模拟报错
            int i = 1 / 0;
            return 1;
        }).whenComplete((r, e) -> {
            System.out.println("whenComplete被调用了......");
            if (e == null) {
                System.out.println("任务执行完成,r:" + r);
            } else {
                System.out.println("任务执行异常,e:" + e);
            }
        });
        System.out.println(completableFuture.get());
    }

    /**
     * thenCombine：将当前任务和其他任务都执行结束后，拿到这两个任务的执行结果合并处理，回调BiFunction，然后返回新的结果
     */
    @Test
    public void testThenCombine() throws Exception {
        CompletableFuture<Boolean> sendResultFuture = CompletableFuture.supplyAsync(() -> true);
        CompletableFuture<Boolean> saveResultFuture = CompletableFuture.supplyAsync(() -> false);

        CompletableFuture<Integer> completableFuture = sendResultFuture
                .thenCombine(saveResultFuture, (sendStatus, saveStatus) -> {
                    if (!sendStatus) {
                        return 1;
                    }
                    if (!saveStatus) {
                        return 2;
                    }
                    return 0;
                });
        System.out.println(completableFuture.get());
    }

    /**
     * 以Async结尾的方法：一般对应会有两个Async结尾的方法
     * <ul>
     *      <li>1.例如thenApply跟thenApplyAsync方法，主要区别是执行任务是否开启异步线程来执行；thenApplyAsync会重新开启一个线程来执行下一个任务</li>
     * ，而thenApply还是用上一阶段任务执行的线程来执行
     *      <li>2.两个Async方法区别，一个是使用默认的线程池来执行任务（ForkJoinPool）；另一个是方法参数传入的线程池来执行任务</li>
     * </ul>
     */
    @Test
    public void testAsync() throws Exception {
        // 使用公共线程池ForkJoinPool
        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> 1)
                .thenApplyAsync(r -> {
                    System.out.println("上一步任务执行结果:" + r);
                    return r;
                });
        System.out.println(completableFuture1.get());

        // 使用自定义线程池
        CompletableFuture<Integer> voidCompletableFuture2 = CompletableFuture.supplyAsync(() -> 2)
                .thenApplyAsync(r -> {
                    System.out.println("上一步任务执行结果:" + r);
                    return r;
                }, executorService);
        System.out.println(voidCompletableFuture2.get());
    }
}
